﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleORM.Infrastructure.Swagger
{
    /// <summary>
    /// 数据签名认证
    /// </summary>
    /// <description>添加对数据签名的认证，初始提供较简单的验证算法，如有更改该，可自行添加<description>
    /// <author>yun</author>
    /// <created>2023-07-09</created>
    /// <modified>2023-07-09</modified>
    public class RequestValidSignMiddleware
    {
        private readonly RequestDelegate _next;

        public RequestValidSignMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var model = new RequestValidSignModel();

            //获取请求体  
            string requestBody = await ReadRequestBodyAsync(context.Request.BodyReader) ?? "";
            //1.先验证AppId是不是有,没有就直接返回失败
            //2.如果有的话,就去缓存里取AppID对应的配置(如果缓存里没有,就去配置文件里取)
            //3.如果没有对应AppId的配置,说明不是正确的请求,返回失败
            //model.AppId = context.Request.Headers["AppId"];
            //if (String.IsNullOrEmpty(model.AppId))
            //{
            //    await this.ResponseValidFailedAsync(context, 501);
            //    return;
            //}
            //var cacheSvc = context.RequestServices.GetRequiredService<IMemoryCache>(); //使用缓存应用
            //var cacheAppIdKey = $"RequestValidSign:APPID:{model.AppId}";
            //var curConfig = cacheSvc.GetOrCreate<AppConfigModel>(cacheAppIdKey, (e) =>
            //{
            //    e.SlidingExpiration = TimeSpan.FromHours(1);
            //    var configuration = context.RequestServices.GetRequiredService<IConfiguration>();
            //    var listAppConfig = configuration.GetSection(AppConfigModel.ConfigSectionKey).Get<AppConfigModel[]>();
            //    return listAppConfig.SingleOrDefault(x => x.AppId == model.AppId);
            //});
            //if (curConfig == null)
            //{
            //    await this.ResponseValidFailedAsync(context, 502);
            //    return;
            //}
            //1.把缓存/配置里面的APP配置取出来,拿到AppSecret
            //2.如果请求里附带了AppSecret(调试用),那么就只验证AppSecret是否正确
            //3.传过来的AppSecret必需是Base64编码后的
            //4.然后比对传过来的AppSecret是否与配置的AppSecret一至,如果一至就通过,不一至就返回失败

            //5.如果请求里没有附带AppSecret,那么走其它验证逻辑.
            //model.AppSecret = curConfig.AppSecret;
            var headerSecret = context.Request.Headers["AppSecret"].ToString();
            if (!String.IsNullOrEmpty(headerSecret))
            {
                var secretBuffer = new byte[1024];
                var secretIsBase64 = Convert.TryFromBase64String(headerSecret, new Span<byte>(secretBuffer), out var bytesWritten);
                string ss = Encoding.UTF8.GetString(secretBuffer, 0, bytesWritten);
                //添加了一个简单签名验证，可自行添加其他算法，续前后端，或与第三方协调一致
                if (secretIsBase64 && Encoding.UTF8.GetString(secretBuffer, 0, bytesWritten) == "123456")
                    await _next(context);
                else
                {
                    await this.ResponseValidFailedAsync(context, 508);
                    return;
                }
            }
            else
            {
                //1.验证时间戳是不是有在请求头里传过来，没有就返回失败
                //2.验证时间戳与当前时间比较，如果不在过期时间(5分钟)之内的请求，就返回失败
                //3.过期时间自行商量决定
                //4.时间戳为1970年1月1日到现在时间的毫秒数（UTC时间）
                var ticksString = context.Request.Headers["Ticks"].ToString();
                if (String.IsNullOrEmpty(ticksString))
                {
                    await this.ResponseValidFailedAsync(context, 503);
                    return;
                }
                model.Ticks = long.Parse(context.Request.Headers["Ticks"].ToString());
                var diffTime = DateTime.UtcNow - (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(model.Ticks));
                var expirTime = TimeSpan.FromSeconds(300);//过期时间
                if (diffTime > expirTime)
                {
                    await this.ResponseValidFailedAsync(context, 504);
                    return;
                }
                //1.验证请求ID是不是有在请求头里传过来，没有就返回失败
                //2.验证请求ID是不是已经在缓存里存在，如果存在就表示重复请求，那么就返回失败
                //3.如果请求ID在缓存中不存在，那么就表示正常的请求，同时把请求ID添加到缓存
                model.RequestId = context.Request.Headers["RequestId"].ToString();
                if (String.IsNullOrEmpty(model.RequestId))
                {
                    await this.ResponseValidFailedAsync(context, 505);
                    return;
                }
                var cacheKey = $"RequestValidSign:RequestId:{model.AppId}:{model.RequestId}";
                //if (cacheSvc.TryGetValue(cacheKey, out _))
                //{
                //    await this.ResponseValidFailedAsync(context, 506);
                //    return;
                //}
                //else
                //    cacheSvc.Set(cacheKey, model.RequestId, expirTime);
                //1.验证签名是否正常
                //2.签名字符串是$"{AppId}{Ticks}{RequestId}{AppSecret}"组成
                //3.然后把签名字符串做MD5，再与请求传过来的Sign签名对比
                //4.如果一至就表示正常请求，请求通过。如果不一至，返回失败
                model.Sign = context.Request.Headers["Sign"].ToString();
                //签名算法示例
                //var param = new SortedDictionary<string, string>(new AsciiComparer());
                // param.Add("data", @$"data:{data}");
                //param.Add("req_id", @$"req_id:{req_id}");
                //param.Add("appKey", @$"appKey;{appKey}");
                // param.Add("timestamp", $@"timestamp:{timestamp}");
                //string currentSign = SignHelper.GetSign(param, appKey);

                if (!model.Valid())//这里是实体类的构造函数，内含实现方法，可以自定义
                {
                    await this.ResponseValidFailedAsync(context, 507);
                    return;
                }
                await _next(context);
            }
        }
        /// <summary>
        /// 返回验证失败
        /// </summary>
        /// <param name="context"></param>
        /// <param name="status"></param>
        /// <returns></returns>
        public async Task ResponseValidFailedAsync(HttpContext context, int status)
        {
            context.Response.StatusCode = 500;
            await context.Response.WriteAsJsonAsync(new { Success = false, Status = status, Msg = "请求签名验证失败" }, null, context.RequestAborted);
        }

        /// <summary>
        ///获取请求体
        /// </summary>
        /// <param name="bodyReader"></param>
        /// <returns></returns>
        private async Task<string> ReadRequestBodyAsync(PipeReader bodyReader)
        {
            // 设置缓冲区大小
            StringBuilder requestBodyBuilder = new StringBuilder();

            while (true)
            {
                // 读取请求体数据
                ReadResult readResult = await bodyReader.ReadAsync();
                ReadOnlySequence<byte> bufferSequence = readResult.Buffer;

                if (bufferSequence.IsEmpty && readResult.IsCompleted)
                {
                    break;
                }

                foreach (ReadOnlyMemory<byte> segment in bufferSequence)
                {
                    requestBodyBuilder.Append(Encoding.UTF8.GetString(segment.Span));
                }

                // 标记已读取的范围
                //bodyReader.AdvanceTo(bufferSequence.End);//不能用这个，这个不能复位指针
                bodyReader.AdvanceTo(bufferSequence.Start, bufferSequence.End);//用这个，这个可以复位指针

                if (readResult.IsCompleted)
                {
                    break;
                }
            }

            return requestBodyBuilder.ToString();
        }

    }
    public class AppConfigModel
    {
        public const string ConfigSectionKey = "AppConfig";//秘钥
        /// <summary>
        /// 应用Id
        /// </summary>
        public string AppId { get; set; }

        /// <summary>
        /// 应用密钥
        /// </summary>
        public string AppSecret { get; set; }

    }
    public class RequestValidSignModel : AppConfigModel
    {
        public static string Publickey = "031A3F2719CB991BF6849D3F8A535E7C";
        /// <summary>
        /// 前端时间戳
        /// Date.now()
        /// 1970 年 1 月 1 日 00:00:00 (UTC) 到当前时间的毫秒数
        /// </summary>
        public long Ticks { get; set; }
        /// <summary>
        /// 请求ID
        /// </summary>
        public string RequestId { get; set; }
        /// <summary>
        /// 签名
        /// </summary>
        public string Sign { get; set; }

        /// <summary>
        /// 请求资源
        /// </summary>
        public string Method { get; set; }

        /// <summary>
        /// 租户ID
        /// </summary>
        public string Req_id { get; set; }

        /// <summary>
        /// 随机数
        /// </summary>
        public string RDmber { get; set; }
        public bool Valid()
        {
            var validStr = $"{Ticks}{RequestId}{AppSecret}";// $"{AppId}{Ticks}{RequestId}{AppSecret}";

            var param = new SortedDictionary<string, string>(new AsciiComparer());
            param.Add("Ticks", @$"Ticks:{Ticks}");
            param.Add("RequestId", @$"RequestId:{RequestId}");
            param.Add("Method", @$"Method;{Method}");
            param.Add("Req_id", $@"Req_id:{Req_id}");
            param.Add("RDmber", $@"RDmber:{RDmber}");
            var date = TransToDateTime(Ticks);
            //请求10秒超时，签名失效
            if (date <= DateTime.UtcNow.AddSeconds(-10000))
            {
                return false;
            }
            //return true;
            return SignHelper.GetSign(param, Publickey) == Sign;
            //return validStr.GetMD5() != Sign;//简单算法，正确是 ==，为测试改成！=
        }

        /// <summary>
        /// 时间戳转换成时间
        /// </summary>
        /// <param name="trans"></param>
        /// <returns></returns>
        private DateTime TransToDateTime(long trans)
        {
            DateTime date = new DateTime();
            if (trans.ToString().Length == 13)
            {
                date = new DateTime(1970, 1, 1, 8, 0, 0).AddMilliseconds(trans);
            }
            else
            {
                date = new DateTime(1970, 1, 1, 8, 0, 0).AddSeconds(trans);
            }
            return date;
        }
    }

    /// <summary>
    /// 中间件注册扩展 写一个中间件的扩展,这样我们在Program里可以方便的使用/停用中间件
    /// </summary>
    public static class RequestValidSignMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestValidSign(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestValidSignMiddleware>();
        }

        //private async Task<string> ReadRequestBodyAsync(ReadableBuffer bodyReader)
        //{
        //    // 设置缓冲区大小
        //    byte[] buffer = new byte[4096];
        //    StringBuilder requestBodyBuilder = new StringBuilder();

        //    while (true)
        //    {
        //        // 读取请求体数据
        //        ReadResult readResult = await bodyReader.ReadAsync();
        //        ReadOnlySequence<byte> bufferSequence = readResult.Buffer;

        //        if (bufferSequence.IsEmpty && readResult.IsCompleted)
        //        {
        //            break;
        //        }

        //        foreach (ReadOnlyMemory<byte> segment in bufferSequence)
        //        {
        //            requestBodyBuilder.Append(Encoding.UTF8.GetString(segment.Span));
        //        }

        //        // 标记已读取的范围
        //        bodyReader.AdvanceTo(bufferSequence.End);

        //        if (readResult.IsCompleted)
        //        {
        //            break;
        //        }
        //    }

        //    return requestBodyBuilder.ToString();
        //}
    }
}
